/*
Michael McArthur

This is the main code that deals with starting the game, updating game state, and drawing.
The "setShaders()" function was adopted from the Lighthouse3D GLSL tutorial at
http://www.lighthouse3d.com/opengl/glsl/.
*/


#include <GL/glew.h>
#include <stdlib.h>
#include <GL/glut.h>
#include <stdio.h>
#include <vector>
#include <algorithm>
#include <iostream>
#include <string>

#include "MovableObject.h"
#include "Collidable.h"
#include "Platform.h"
#include "Camera.h"
#include "BMPLoader.h"
#include "Enemy.h"
#include "Player.h"
#include "textfile.h"

using namespace std;

const int wWIDTH = 800;
const int wHEIGHT = 600;

const GLfloat ambientL[] = {1,0.5,0,1};

GLfloat rtri = 1;
GLfloat rquad = 1;

Player* player;
vector<Collidable*> plats2;

Camera* c = NULL;

GLuint v,f,f2,p;

void init (void)

{
	c = new Camera();
	player = new Player();
	player->setScale(15,15,15);

	Enemy* e = new Enemy();
	e->setScale(15,15,15);
	e->setPosition(1700,500,0);

	Enemy* e2 = new Enemy();
	e2->setScale(15,15,15);
	e2->setPosition(800,500,0);

	Enemy* e3 = new Enemy();
	e3->setScale(15,15,15);
	e3->setPosition(2500,500,0);

	Enemy* e4 = new Enemy();
	e4->setScale(40,40,40);
	e4->setPosition(10000,2000,0);
	e4->setMoveSpeed(25);


	plats2.push_back(e);

	plats2.push_back(e2);

	plats2.push_back(e3);

	plats2.push_back(e4);
	
	

	Platform* p2 = new Platform(500,2000);
	p2->setPosition(-1500,1450,0);

	Platform* p = new Platform(1000,50);
	p->setPosition(0,-500,0);

	Platform* p3 = new Platform(1000,50);
	p3->setPosition(2500,-250,0);

	Platform* p4 = new Platform(1000,50);
	p4->setPosition(4200,300,0);
	
	Platform* p5 = new Platform(5000,100);
	p5->setPosition(10000,-500,0);
	
	plats2.push_back(p2);
	plats2.push_back(p);
	plats2.push_back(p3);
	plats2.push_back(p4);
	plats2.push_back(p5);

	c->setLookFrom(Vector3(0,0,3000));//2500));

	glClearColor (0,0,0, 0.0);
	
	//Enable normalization of normal vectors
	glEnable(GL_NORMALIZE);

	// Set up projection
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(45,wWIDTH/(float)wHEIGHT,1,5000); //10000

	
	//Enable lighting
	glEnable(GL_LIGHTING);
	GLfloat lightDiff[] = {1,1,1,1};
	glLightfv(GL_LIGHT0,GL_DIFFUSE,lightDiff);

	glEnable(GL_LIGHT0);
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT,ambientL);
	//glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER,GL_TRUE);
	
	//Shading
	glShadeModel(GL_SMOOTH);
	
	// No repeating key presses!
	glutIgnoreKeyRepeat(true);
}

/*
Initialize phong shaders.  This code is adapted from the Lighthouse 3D GLSL tutorial
at http://www.lighthouse3d.com/opengl/glsl/
*/
void setShaders() {

	char *vs = NULL,*fs = NULL,*fs2 = NULL;
	v = glCreateShader(GL_VERTEX_SHADER);
	f = glCreateShader(GL_FRAGMENT_SHADER);

	vs = textFileRead("phong.vert");
	fs = textFileRead("phong.frag");

	const char * ff = fs;
	const char * vv = vs;

	glShaderSource(v, 1, &vv,NULL);
	glShaderSource(f, 1, &ff,NULL);

	free(vs);free(fs);

	glCompileShader(v);
	glCompileShader(f);

	p = glCreateProgram();
	glAttachShader(p,f);
	glAttachShader(p,v);

	glLinkProgram(p);
	glUseProgram(p);
}

void keyHandler(unsigned char key, int x, int y)
{
	switch (key){
		case 'r':
			for (vector<Collidable*>::iterator it = plats2.begin(); it != plats2.end();)
			{
				delete (*it);
				it = plats2.erase(it);
			}
			delete c;
			delete player;
			init();
			break;
		default:
			player->handleKey((int)key, true);
	}
}

void upKey(unsigned char key, int x, int y)
{
	player->handleKey((int)key, false);
}

void specialKeys(int key, int x, int y)
{
	player->handleKey(key,true);
}

void specialUp(int key, int x, int y)
{
	player->handleKey(key,false);
}

void exitFrame()
{
	glutPostRedisplay();
}

// Animate characters and move the camera
void updateState(int value)
{
	for (vector<Collidable*>::iterator it = plats2.begin(); it != plats2.end();)
	{
		if ( (*it)->dead )
		{
			cout << "DELETING ENEMY" << endl;
			delete (*it);
			
			it = plats2.erase(it);
		}else{
			if ((*it)->type == ENEMY)
			{
				Enemy* p2e = dynamic_cast<Enemy*>((*it));
				p2e->move(plats2);
			}
			it++;
		}
		
	}
	player->move(plats2);
	c->update(*player);
	glutTimerFunc(	16.66, updateState, 0 );
}

void display(void)

{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	c->drawSky();

	glClear(GL_DEPTH_BUFFER_BIT);	// Sky elements are in the background
	
	// Draw enemies and platforms
	for (vector<Collidable*>::iterator it = plats2.begin(); it != plats2.end(); it++)
	{
		(*it)->draw();
	}

	// Draw player
	player->draw();

	// Position light
	GLfloat lightPos[] = {100,100,500,0};
	glLightfv(GL_LIGHT0,GL_POSITION,lightPos);

	glFlush();
	glutSwapBuffers();
}


int main(int argc, char** argv)

{

	glutInit(&argc, argv);

	glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);

	glutInitWindowSize (wWIDTH, wHEIGHT);

	glutInitWindowPosition (0, 0);

	glutCreateWindow ("Some awesome game!");

	init();

	glutDisplayFunc(display);

	glutTimerFunc(	33.33, updateState, 0 );	// Update game logic/animations

	glutKeyboardFunc(keyHandler);

	glutSpecialFunc(specialKeys);		//handle arrow keys

	glutKeyboardUpFunc(upKey);

	glutSpecialUpFunc(specialUp);

	glutIdleFunc(exitFrame);					// Repaint

	glClearDepth(1.0f);
	glEnable(GL_DEPTH_TEST);							// Enables Depth Testing
	glDepthFunc(GL_LEQUAL);

	//shader setup
	GLenum err = glewInit();
	if (GLEW_OK != err)
	{
		fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
	}
	if (glewIsSupported("GL_VERSION_2_0"))
		printf("Ready for OpenGL 2.0\n");
	else {
		printf("OpenGL 2.0 not supported\n");
		exit(1);
	}
	setShaders();
		

	glutMainLoop();

	return 0; /* ISO C requires main to return int. */

}